package com.icesoft.excel;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.poi.excel.*;
import cn.hutool.poi.excel.cell.CellUtil;
import cn.hutool.poi.excel.style.StyleUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public enum TemplateHelper {
    INSTANCE;
    Configuration cfg;
    private static final String ROW_SPLIT = "|==========|\n";
    private static final String CELL_SPLIT = "|----------|\t";

    TemplateHelper() {
        cfg = new Configuration(Configuration.VERSION_2_3_28); // 通过Freemaker的Configuration读取相应的ftl
        cfg.setEncoding(Locale.CHINA, "utf-8");
        // 使用 []做为语法符号
        cfg.setTagSyntax(Configuration.SQUARE_BRACKET_TAG_SYNTAX);
        // 设定去哪里读取相应的ftl模板文件
        cfg.setClassForTemplateLoading(TemplateHelper.class, "/ftl");
        //cfg.setDirectoryForTemplateLoading(new File(ftlPath));
    }

    /**
     * 通过模板名称获取 Template 解析对象
     */
    public static Template getTemplateEngine(String ftlName) throws IOException {
        return INSTANCE.cfg.getTemplate(ftlName);
    }

    /**
     * 通过模板代码获取 Template 解析对象
     */
    public static Template getTmpStrTemplate(String templateCode) throws IOException {
        String name = DigestUtil.md5Hex(templateCode);
        Template template;
        try {
            template = getTemplateEngine(name);
        } catch (TemplateNotFoundException e) {
            log.info("{}={}", name, templateCode);
            System.out.println(templateCode);
            template = new Template(name, templateCode, INSTANCE.cfg);
        }
        return template;
    }

    /**
     * 解析模板名称
     */
    public static String processFtl(String ftl, Object context) throws IOException, TemplateException {
        StringWriter stringWriter = new StringWriter();
        TemplateHelper.getTemplateEngine(ftl).process(context, stringWriter);
        return stringWriter.toString();
    }

    /**
     * 解析模板代码
     */
    public static String processCode(String templateCode, Object context) throws IOException, TemplateException {
        StringWriter stringWriter = new StringWriter();
        TemplateHelper.getTmpStrTemplate(templateCode).process(context, stringWriter);
        return stringWriter.toString();
    }

    /**
     * 解析excel模板代码文件
     */
    public static void processExcelTpl(File excelTplFile, File destFile, Object context) throws IOException, TemplateException {
        FileUtil.del(destFile);
        Stack<String> stack;
        StringBuilder codeBuilder;
        boolean isCodeBlock;
        WorkbookUtil.createBookForWriter(destFile);
        try (ExcelWriter excelWriter = ExcelUtil.getWriter(destFile); ExcelReader excelReader = ExcelUtil.getReader(excelTplFile)) {
            Sheet tplSheet = excelReader.getSheet();
            Sheet outSheet = excelWriter.getSheet();
            //统计开始和结束代码块
            stack = new Stack<>();
            //保存代码块
            codeBuilder = new StringBuilder();
            //是否进入代码块模式
            isCodeBlock = false;
            Map<String, Row> rowMap = new HashMap<>();
            int outRowIdx = 0;
            for (int i = 0; i < tplSheet.getLastRowNum(); i++) {
                Row row = tplSheet.getRow(i);
                if (row == null) {
                    continue;
                }
                String key = DigestUtil.md5Hex(row.toString());
                rowMap.put(key, row);
                List<Cell> newCellList = new ArrayList<>();
                //fill row cell
                for (int j = 0; j < row.getLastCellNum(); j++) {
                    Cell cell = row.getCell(j);
                    if (cell == null) {
                        newCellList.add(null);
                        continue;
                    }

                    newCellList.add(cell);
                    String text = String.valueOf(CellUtil.getCellValue(cell)).trim();
                    List<String> startTags = getStartTags(text);
                    List<String> endTags = getEndTags(text);
                    if (!startTags.isEmpty()) {
                        stack.addAll(startTags);
                        isCodeBlock = true;
                    }
                    if (!endTags.isEmpty()) {
                        if (stack.isEmpty()) {
                            throw new IllegalArgumentException("not fund start tag for end tag, in " + i + "," + j + ":" + text);
                        }
                        for (String ignored : endTags) {
                            stack.pop();
                        }

                    }
                    if (isCodeBlock) {
                        //标签深度做为key值
                        if (j == 0) {
                            //第一个数据为深度key标识位
                            codeBuilder.append(key);
                        }
                        codeBuilder.append(CELL_SPLIT).append(text);
                        continue;
                    }

                    //解析单个cell模板数据
                    if (!isTpl(text)) {
                        continue;
                    }
                    String val = TemplateHelper.processCode(text, context);
                    cell.setCellValue(val);

                }
                if (isCodeBlock) {
                    newCellList.clear();
                    codeBuilder.append(ROW_SPLIT);
                    if (stack.isEmpty()) {
                        //代码块结束，开始解析模板
                        String data = TemplateHelper.processCode(codeBuilder.toString(), context);
                        log.debug("\nstart----------------\n{}----------------\n{}end----------------", codeBuilder, data);
                        int newRowSize = newRowData(outSheet, outRowIdx, rowMap, data);
                        //跳转到插入的新行之后
                        outRowIdx += newRowSize;
                        //重置数据
                        codeBuilder.setLength(0);
                        isCodeBlock = false;
                    }
                } else {
                    RowUtil.insertRow(outSheet, outRowIdx, 1);
                    Row outRow = outSheet.getRow(outRowIdx);
                    outRow.setHeight(row.getHeight());
                    for (int j = 0; j < newCellList.size(); j++) {
                        Cell cell = newCellList.get(j);
                        if (cell == null) {
                            continue;
                        }
                        //复制样式，设置值
                        Cell newCell = outRow.createCell(j, cell.getCellType());
                        CellUtil.setCellValue(newCell, CellUtil.getCellValue(cell), StyleUtil.cloneCellStyle(newCell, cell.getCellStyle()));
                        //合并单元格
                        CellRangeAddress ca = getMergedRegion(cell.getSheet(), cell.getColumnIndex(), cell.getRowIndex());
                        if (ca != null) {
                            if (!CellUtil.isMergedRegion(newCell)) {
                                CellUtil.mergingCells(newCell.getSheet(), outRowIdx, ca.getLastRow() - ca.getFirstRow() + outRowIdx, ca.getFirstColumn(), ca.getLastColumn());
                            }
                        }
                        newCell.getSheet().setColumnWidth(newCell.getColumnIndex(), cell.getSheet().getColumnWidth(cell.getColumnIndex()));
                    }
                    outRowIdx++;
                }

            }
        }
        if (isCodeBlock) {
            throw new IllegalArgumentException("error tpl end tag： " + stack.toString());
        }

    }

    public static CellRangeAddress getMergedRegion(Sheet sheet, int x, int y) {
        final int sheetMergeCount = sheet.getNumMergedRegions();
        CellRangeAddress ca;
        for (int i = 0; i < sheetMergeCount; i++) {
            ca = sheet.getMergedRegion(i);
            if (y >= ca.getFirstRow() && y <= ca.getLastRow()
                    && x >= ca.getFirstColumn() && x <= ca.getLastColumn()) {
                return ca;
            }
        }
        return null;
    }

    public static Pattern TPL_TAG_PATTERN = Pattern.compile("\\$\\{[^}]+}");

    public static boolean isTpl(String text) {
        Matcher m = TPL_TAG_PATTERN.matcher(text);
        return m.matches();
    }

    public static Pattern START_TAG_PATTERN = Pattern.compile("\\[#[^]]+]");
    public static Pattern END_TAG_PATTERN = Pattern.compile("\\[/#\\w+]");

    public static List<String> getStartTags(String text) {
        Matcher m = START_TAG_PATTERN.matcher(text);
        List<String> list = new ArrayList<>();
        while (m.find()) {
            list.add(m.group());
        }
        return list;
    }

    public static List<String> getEndTags(String text) {
        Matcher m = END_TAG_PATTERN.matcher(text);
        List<String> list = new ArrayList<>();
        while (m.find()) {
            list.add(m.group());
        }
        return list;
    }

    private static int newRowData(Sheet sheet, int curRowIdx, Map<String, Row> rowMap, String data) {
        //fill new row
        List<String> rowDataList = StrUtil.split(data, ROW_SPLIT);
        int newRowSize = 0;
        for (String rowData : rowDataList) {
            List<String> cellDataList = StrUtil.split(rowData, CELL_SPLIT);
            if (cellDataList.size() == 0) {
                continue;
            }
            //第一个数据是模板样式row map的key
            String key = cellDataList.remove(0);
            if (StrUtil.isBlank(key)) {
                continue;
            }

            Row row = rowMap.get(key);
            if (row.getLastCellNum() != cellDataList.size()) {
                throw new RuntimeException("process new row data error:" + rowData);
            }
            //跳过空白行，需要注意与rowTplListMap的序号对应
            if (StrUtil.isAllBlank(cellDataList.toArray(new CharSequence[0]))) {
                continue;
            }
            int curRow = curRowIdx + newRowSize;
            RowUtil.insertRow(sheet, curRow, 1);
            Row newRow = RowUtil.getOrCreateRow(sheet, curRow);
            newRow.setHeight(row.getHeight());
            newRowSize++;
            for (int c = 0; c < cellDataList.size(); c++) {
                Cell cell = row.getCell(c);
                Cell newCell = newRow.createCell(c, cell.getCellType());
                CellUtil.setCellValue(newCell, cellDataList.get(c), StyleUtil.cloneCellStyle(newCell, cell.getCellStyle()));

                CellRangeAddress ca = getMergedRegion(cell.getSheet(), cell.getColumnIndex(), cell.getRowIndex());
                if (ca != null) {
                    if (!CellUtil.isMergedRegion(newCell)) {
                        CellUtil.mergingCells(newCell.getSheet(), curRow, curRow, ca.getFirstColumn(), ca.getLastColumn());
                    }
                }
                newCell.getSheet().setColumnWidth(newCell.getColumnIndex(), cell.getSheet().getColumnWidth(cell.getColumnIndex()));
            }
        }
        return newRowSize;
    }

}